package com.huawei.cloud.phone.management.application.connection;

import com.huawei.cloud.phone.management.application.connection.dto.ConnectionDTO;
import com.huawei.cloud.phone.management.application.connection.dto.EventDTO;
import com.huawei.cloud.phone.management.application.connection.dto.EventInfoDTO;
import com.huawei.cloud.phone.management.common.exception.InvalidParamException;
import com.huawei.cloud.phone.management.common.exception.NoIdlePhoneException;
import com.huawei.cloud.phone.management.common.exception.NoPermissionException;
import com.huawei.cloud.phone.management.domain.connection.Connection;
import com.huawei.cloud.phone.management.domain.connection.ConnectionDomainService;
import com.huawei.cloud.phone.management.domain.connection.ConnectionParam;
import com.huawei.cloud.phone.management.domain.connection.status.ConnectionState;
import com.huawei.cloud.phone.management.domain.connection.status.ConnectionStateFactory;
import com.huawei.cloud.phone.management.domain.phone.PhoneDomainService;
import com.huawei.cloud.phone.management.domain.phone.PhoneUseStatus;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import java.util.Optional;

@Slf4j
@AllArgsConstructor
@Component
public class ConnectionServiceImpl implements ConnectionService {
    private final ConnectionDomainService connectionDomainService;

    private final PhoneDomainService phoneDomainService;

    @Override
    public ConnectionDTO getConnection(@NonNull String userId, @NonNull String userPhoneId, @NonNull ConnectionDTO.ClientType clientType) {
        log.info("get connection userId: {}, userPhoneId:{}, clientType: {}", userId, userPhoneId, clientType);
        if (!phoneDomainService.isUserPhone(userId, userPhoneId)) {
            throw new NoPermissionException(userPhoneId);
        }

        Optional<Connection> connection = connectionDomainService.allocateIdleConnection(userPhoneId);
        if (!connection.isPresent()) {
            throw new NoIdlePhoneException();
        }
        ConnectionParam connectionParam = new ConnectionParam();
        connectionDomainService.cacheConnectionInfo(connection.get().getPhoneId(), connection.get().getSessionId());
        return new ConnectionDTO(clientType, connectionParam, connection.get());
    }

    @Override
    public void reportEvent(@NonNull EventInfoDTO eventInfoDTO) {
        if (!"app".equals(eventInfoDTO.getEventType())) {
            throw new InvalidParamException("event-type", eventInfoDTO.getEventType());
        }

        EventDTO event = eventInfoDTO.getEvent();
        connectionDomainService.auth(event.getPhoneId(), event.getSessionId());
        if (event.getStatus() == EventDTO.LOG_IN_STATUS) {
            log.info("{} with session {} login", event.getPhoneId(), event.getSessionId());
            ConnectionStateFactory.getState(event.getPhoneId()).connect();
        } else if (event.getStatus() == EventDTO.LOG_OUT_STATUS) {
            log.info("{} with session {} logout", event.getPhoneId(), event.getSessionId());
            connectionDomainService.clearConnectionInfo(event.getPhoneId());
            ConnectionStateFactory.getState(event.getPhoneId()).disconnect();
        }
    }

    @Override
    public void heartbeat(@NonNull EventInfoDTO eventInfoDTO) {
        if (!"heartbeat".equals(eventInfoDTO.getEventType())) {
            throw new InvalidParamException("event-type", eventInfoDTO.getEventType());
        }
        EventDTO event = eventInfoDTO.getEvent();
        phoneDomainService.updatePhoneUseStatus(event.getPhoneId(), PhoneUseStatus.NORMAL);
        if (StringUtils.isEmpty(event.getSessionId())) {
            log.debug("{} heart without session", event.getPhoneId());
            connectionDomainService.clearConnectionInfo(event.getPhoneId());
            ConnectionStateFactory.getOptionalState(event.getPhoneId()).ifPresent(ConnectionState::heartAbnormal);
        } else {
            log.debug("{} heart with session {}", event.getPhoneId(), event.getSessionId());
            connectionDomainService.auth(event.getPhoneId(), event.getSessionId());
            ConnectionStateFactory.getState(event.getPhoneId()).heartNormal();
        }
    }
}
